"use strict";
const Card = require("./Card");
const CardBox = require("./CardBox");
const Table = require("./Table");
const Player = require("./Player");
const PLAYER_STATUS = Player.STATUS;
const PlayerList = Player.PlayerList;
const rule = require("./rule");

const Events = require("./Events");
const SPECIAL_CARD_LIST = [
	["three_straight", 21, 3],
	["three_flush", 22, 3],
	["six_pair", 23, 3],
	["all_small", 24, 3],
	["all_big", 25, 3],
	["one_long", 26, 23],
	["king_long", 27, 49]
];
const SPECIAL_CARD_MAP = new Map();
const SPECIAL_SCORE_MAP = new Map();
const CUSTOME_CARDBOX_GENERATOR_MAP = new Map();
SPECIAL_CARD_LIST.forEach(([handle_name, handle_weight, special_score]) => {
	SPECIAL_CARD_MAP.set(handle_name, handle_weight);
	SPECIAL_SCORE_MAP.set(handle_weight, special_score);
});

function* processGenerator(PLAYER_LIST, FLOW) {
	const TABLE = new Table();
	console.log("FLOW START:", FLOW.id);
	const CARDBOX = FLOW.id && CUSTOME_CARDBOX_GENERATOR_MAP.has(FLOW.id)
		? CUSTOME_CARDBOX_GENERATOR_MAP.get(FLOW.id)(PLAYER_LIST)
		: new CardBox();
	console.log("清空手牌");
	for (let player of PLAYER_LIST) {
		player.removeCards(player.card_list.slice());
	}
	console.log("开始发牌");
	CARDBOX.moveCardsToPlayers(PLAYER_LIST);

	const PLAYER_RES = new Array(PLAYER_LIST.length);
	const PLAYER_RES_SCORES = new WeakMap();
	const PLAYER_RES_CARDS = new WeakMap();
	let player_res_scores_num = 0;
	while (true) {
		const [player, submit_res] = yield "WAIT_FOR_SUBMIT";
		if (PLAYER_RES_SCORES.has(player)) {
			yield new Error("玩家已经提交过，不可重复提交");
			continue;
		}
		if (!PLAYER_LIST.includes(player)) {
			yield new Error("此玩家不在本场游戏中");
			continue;
		}
		const submit_res_cards = FLOW.formatSubmitRes(submit_res);
		if (!player.hasCards(submit_res_cards.all)) {
			yield new Error("存在作弊情况，提交了玩家不拥有的纸牌");
			continue;
		}

		const submit_res_scores = {
			is_special: false,
			type: ""
		};
		// 检测报到牌
		if (submit_res.type) {
			const checkHandle = rule[`is_${submit_res.type}`];
			if (checkHandle && SPECIAL_CARD_MAP.has(submit_res.type)) {
				if (checkHandle(submit_res_cards.all, submit_res_scores)) {
					submit_res_scores.is_special = true;
					const handle_weight = SPECIAL_CARD_MAP.get(submit_res.type);
					["top", "mid", "btm"].forEach(key => {
						submit_res_scores[key] = {
							handle_weight
						};
					});
				} else {
					yield new Error("手牌不是指定的特殊牌型");
					continue;
				}
			} else {
				yield new Error("不存在的特殊牌型");
				continue;
			}
		} else {
			if (!FLOW.checkIsSubmitRes(submit_res)) {
				yield new Error("提交的纸牌排列结果有误");
				continue;
			}
			// 检查是否倒水
			for (let submit_res_type of ["mid", "btm"]) {
				for (let info of [
					["straight_flush", 10], //同花顺
					["four", 9], //铁支
					["full_house", 8], //葫芦
					["flush", 7], //同花
					["straight", 6], //顺子
					["three", 5], //三条
					["two_pair", 4], //两对
					["two", 3], //对子
					["any", 2] //散牌
				]) {
					const [handle_name, handle_weight] = info;
					const res_info = {};
					if (
						rule["is_" + handle_name](
							submit_res_cards[submit_res_type],
							res_info
						)
					) {
						res_info.handle_weight = handle_weight;
						submit_res_scores[submit_res_type] = res_info;
						break;
					}
				}
			}
			if (
				submit_res_scores.btm.handle_weight <
					submit_res_scores.mid.handle_weight ||
				(submit_res_scores.btm.handle_weight ===
					submit_res_scores.mid.handle_weight &&
					submit_res_scores.btm.weights_value <
						submit_res_scores.mid.weights_value)
			) {
				yield new Error("中、尾两墩牌发生倒水情况");
				continue;
			}
			for (let info of [
				["three", 5], //三条
				["two", 3], //对子
				["any", 2] //散牌
			]) {
				const [handle_name, handle_weight] = info;
				const res_info = {};
				if (
					rule["is_" + handle_name](submit_res_cards.top, res_info, 3)
				) {
					res_info.handle_weight = handle_weight;
					submit_res_scores.top = res_info;
					break;
				}
			}
			if (
				submit_res_scores.mid.handle_weight <
					submit_res_scores.top.handle_weight ||
				(submit_res_scores.mid.handle_weight ===
					submit_res_scores.top.handle_weight &&
					submit_res_scores.mid.weights_value <
						submit_res_scores.top.weights_value)
			) {
				yield new Error("头、中两墩牌发生倒水情况");
				continue;
			}
		}

		// 保存分数
		PLAYER_RES_SCORES.set(player, submit_res_scores);
		PLAYER_RES_CARDS.set(player, submit_res_cards);
		player_res_scores_num += 1;
		if (player_res_scores_num === PLAYER_LIST.length) {
			break;
		}
	}
	yield "CAN_CALCULATE_RESULTS";
	// 计算分数结果
	// 需要对比的组合;
	console.log("开始对比结果并计分");
	const indexs_list = rule.getListComIndexs(PLAYER_LIST.length, 2);
	const PLAYER_RESULTS = [];
	for (let player of PLAYER_LIST) {
		const submit_res_cards = PLAYER_RES_CARDS.get(player);
		const _submit_res_cards = submit_res_cards.top
			? {
					top: submit_res_cards.top,
					mid: submit_res_cards.mid,
					btm: submit_res_cards.btm
				}
			: {
					all: submit_res_cards.all
				};
		PLAYER_RESULTS.push({
			player_id: player.id,
			one_vs_one_list: [],
			multiple: 1, // 倍数，通杀存在翻倍情况
			extra: 0, // 附加奖励，暂无用到
			get total() {
				let total = 0;
				for (let one_vs_one of this.one_vs_one_list) {
					total +=
						(one_vs_one.top + one_vs_one.mid + one_vs_one.btm) *
							one_vs_one.multiple +
						one_vs_one.extra;
				}
				return total * this.multiple + this.extra;
			},
			submit_res_cards: _submit_res_cards,
			submit_res_scores: PLAYER_RES_SCORES.get(player)
		});
	}
	const player_boom_times_list = new Array(PLAYER_LIST.length).fill(0);
	for (let indexs of indexs_list) {
		const player1_index = indexs[0];
		const player2_index = indexs[1];
		const player1 = PLAYER_LIST[player1_index];
		const player2 = PLAYER_LIST[player2_index];
		const scores1 = PLAYER_RES_SCORES.get(player1);
		const scores2 = PLAYER_RES_SCORES.get(player2);
		const results1 = PLAYER_RESULTS[player1_index];
		const results2 = PLAYER_RESULTS[player2_index];
		const submit_res_cards1 = PLAYER_RES_CARDS.get(player1);
		const submit_res_cards2 = PLAYER_RES_CARDS.get(player2);
		const _submit_res_cards1 = submit_res_cards1.top
			? {
					top: submit_res_cards1.top,
					mid: submit_res_cards1.mid,
					btm: submit_res_cards1.btm
				}
			: {
					all: submit_res_cards1.all
				};
		const _submit_res_cards2 = submit_res_cards2.top
			? {
					top: submit_res_cards2.top,
					mid: submit_res_cards2.mid,
					btm: submit_res_cards2.btm
				}
			: {
					all: submit_res_cards2.all
				};

		const one_vs_one1 = {
			is_special: false,
			player_id: player2.id,
			top: 0,
			mid: 0,
			btm: 0,
			multiple: 1, // 倍数，打枪存在翻倍情况
			extra: 0, // 附加奖励，半打枪存在加分的情况
			submit_res_cards: _submit_res_cards2,
			submit_res_scores: scores2
		};
		results1.one_vs_one_list.push(one_vs_one1);
		const one_vs_one2 = {
			is_special: false,
			player_id: player1.id,
			top: 0,
			mid: 0,
			btm: 0,
			multiple: 1, // 倍数，打枪存在翻倍情况
			extra: 0, // 附加奖励，半打枪存在加分的情况, 特殊牌也存在加分情况
			submit_res_cards: _submit_res_cards1,
			submit_res_scores: scores1
		};
		results2.one_vs_one_list.push(one_vs_one2);
		const bigger_one = new Map();
		let is_special = false;
		if (
			SPECIAL_SCORE_MAP.has(scores1.top.handle_weight) &&
			SPECIAL_SCORE_MAP.has(scores2.top.handle_weight)
		) {
			console.log("二者都是特殊牌，彼此不互相吃");
			is_special = true; // 二者都是特殊牌，彼此不互相吃
			continue;
		} else {
			for (let type of ["top", "mid", "btm"]) {
				const scores1_part = scores1[type];
				const scores2_part = scores2[type];
				if (scores1_part.handle_weight > scores2_part.handle_weight) {
					one_vs_one1[type] += 1;
					one_vs_one2[type] -= 1;
					bigger_one.set(type, [
						one_vs_one1,
						scores1_part,
						one_vs_one2,
						scores2_part
					]);
				} else if (
					scores1_part.handle_weight < scores2_part.handle_weight
				) {
					one_vs_one1[type] -= 1;
					one_vs_one2[type] += 1;
					bigger_one.set(type, [
						one_vs_one2,
						scores2_part,
						one_vs_one1,
						scores1_part
					]);
				} else if (
					scores1_part.weights_value > scores2_part.weights_value
				) {
					one_vs_one1[type] += 1;
					one_vs_one2[type] -= 1;
					bigger_one.set(type, [
						one_vs_one1,
						scores1_part,
						one_vs_one2,
						scores2_part
					]);
				} else if (
					scores1_part.weights_value < scores2_part.weights_value
				) {
					one_vs_one1[type] -= 1;
					one_vs_one2[type] += 1;
					bigger_one.set(type, [
						one_vs_one2,
						scores2_part,
						one_vs_one1,
						scores1_part
					]);
				} else {
					// 打平
					continue;
				}
				if (type === "top") {
					// 特殊牌只额外计分一次
					const [
						bigger_one_vs_one,
						bigger_scores_part,
						smaller_one_vs_one,
						smaller_scores_part
					] = bigger_one.get(type);
					// 特殊牌
					if (
						SPECIAL_SCORE_MAP.has(bigger_scores_part.handle_weight)
					) {
						is_special = true; // 使用特殊牌的计分形式
						const extra_score = SPECIAL_SCORE_MAP.get(
							bigger_scores_part.handle_weight
						);
						bigger_one_vs_one.extra += extra_score;
						smaller_one_vs_one.extra -= extra_score;
					}
					if (
						SPECIAL_SCORE_MAP.has(smaller_scores_part.handle_weight)
					) {
						is_special = true; // 使用特殊牌的计分形式
						const extra_score = SPECIAL_SCORE_MAP.get(
							smaller_scores_part.handle_weight
						);
						bigger_one_vs_one.extra -= extra_score;
						smaller_one_vs_one.extra += extra_score;
					}
				}
			}
		}

		if (is_special) {
			// 特殊牌，直接跳过正常的比分
			one_vs_one1.is_special = true;
			one_vs_one2.is_special = true;
			continue;
		}
		// 判断底部特殊牌
		if (bigger_one.has("btm")) {
			const [
				bigger_one_vs_one,
				bigger_scores_part,
				smaller_one_vs_one
			] = bigger_one.get("btm");
			if (bigger_scores_part.handle_weight === 10) {
				// 同花顺
				bigger_one_vs_one.btm += 4;
				smaller_one_vs_one.btm -= 4;
			} else if (bigger_scores_part.handle_weight === 9) {
				// 铁支
				bigger_one_vs_one.btm += 3;
				smaller_one_vs_one.btm -= 3;
			}
		}
		// 判断中间特殊牌
		if (bigger_one.has("mid")) {
			const [
				bigger_one_vs_one,
				bigger_scores_part,
				smaller_one_vs_one
			] = bigger_one.get("mid");
			if (bigger_scores_part.handle_weight === 10) {
				// 同花顺
				bigger_one_vs_one.mid += 9;
				smaller_one_vs_one.mid -= 9;
			} else if (bigger_scores_part.handle_weight === 9) {
				// 铁支
				bigger_one_vs_one.mid += 7;
				smaller_one_vs_one.mid -= 7;
			} else if (bigger_scores_part.handle_weight === 8) {
				// 葫芦
				bigger_one_vs_one.mid += 1;
				smaller_one_vs_one.mid -= 1;
			}
		}
		// 判断头部特殊牌
		if (bigger_one.has("top")) {
			const [
				bigger_one_vs_one,
				bigger_scores_part,
				smaller_one_vs_one
			] = bigger_one.get("top");
			if (bigger_scores_part.handle_weight === 5) {
				// 三条
				bigger_one_vs_one.top += 2;
				smaller_one_vs_one.top -= 2;
			}
		}

		// 打枪
		{
			if (one_vs_one1.top * one_vs_one1.mid * one_vs_one1.btm === 0) {
				if (
					one_vs_one1.top === 0 &&
					one_vs_one1.mid === 0 &&
					one_vs_one1.btm === 0
				) {
					// 全等，掠过
				} else {
					// 存在半打枪的情况
					if (
						one_vs_one1.top >= 0 &&
						one_vs_one1.mid >= 0 &&
						one_vs_one1.btm >= 0
					) {
						one_vs_one1.extra += 1;
						one_vs_one2.extra -= 1;
					} else if (
						one_vs_one2.top >= 0 &&
						one_vs_one2.mid >= 0 &&
						one_vs_one2.btm >= 0
					) {
						one_vs_one1.extra -= 1;
						one_vs_one2.extra += 1;
					}
				}
			} else if (
				(one_vs_one1.top | one_vs_one1.mid | one_vs_one1.btm) > 0 ||
				(one_vs_one2.top | one_vs_one2.mid | one_vs_one2.btm) > 0
			) {
				// 输赢两者都要翻倍
				one_vs_one1.multiple = 2;
				one_vs_one2.multiple = 2;
				if (one_vs_one1.top > 0) {
					// 记录打枪次数
					player_boom_times_list[player1_index] += 1;
				} else {
					player_boom_times_list[player2_index] += 1;
				}
			}
		}
	}

	let boom_player;
	if (
		player_boom_times_list.some((player_boom_times, player_index) => {
			if (player_boom_times === 3) {
				// 通杀
				console.log("通杀");
				const player = (boom_player = PLAYER_LIST[player_index]);
				const results = PLAYER_RESULTS[player_index];
				// results.multiple = 2;
				results.one_vs_one_list.forEach(one_vs_one => {
					one_vs_one.multiple *= 2;
				});
				return true;
			}
		})
	) {
		const boom_player_id = boom_player.id;
		PLAYER_RESULTS.forEach(results => {
			if (results.player_id !== boom_player_id) {
				results.one_vs_one_list.forEach(one_vs_one => {
					if (one_vs_one.player_id === boom_player_id) {
						one_vs_one.multiple *= 2;
					}
				});
			}
		});
	}
	return PLAYER_RESULTS;
}
class Flow extends Events {
	constructor(player_list, flow_id) {
		super();
		this.id = flow_id;
		this.process = processGenerator(player_list, this);
		this.status = {};
		this.setStatus(this.process.next());
		this.res = null;
	}
	playerSubmitRes(player, submit_res) {
		if (this.status.value === "WAIT_FOR_SUBMIT") {
			const res = this.process.next([player, submit_res]);
			if (res.value instanceof Error) {
				this.setStatus(this.process.next());
				throw res.value;
			}
			this.setStatus(res);
		}
	}
	setStatus(status) {
		if (this.status.value !== status.value) {
			this.status = status;
			this.emitPropChanged("status");
			if (status.done) {
				this.emit("end", status.value);
			}
		}
	}
	checkIsSubmitRes(submit_res) {
		if (
			Array.isArray(submit_res.top) &&
			submit_res.top.length === 3 &&
			Array.isArray(submit_res.mid) &&
			submit_res.mid.length === 5 &&
			Array.isArray(submit_res.btm) &&
			submit_res.btm.length === 5
		) {
			return true;
		}
	}
	formatSubmitRes(submit_res) {
		const res = {
			type: submit_res.type
		};
		const all = (res.all = []);
		["top", "mid", "btm", "all"].forEach(key => {
			if (submit_res[key]) {
				res[key] = submit_res[key].map(card_txt =>
					Card.getIns(card_txt)
				);
				if ("all" !== key) {
					all.push(...res[key]);
				}
			}
		});
		return res;
	}
	calculateResults() {
		if (this.status.value === "CAN_CALCULATE_RESULTS") {
			const res = this.process.next();
			this.setStatus(res);
			this.res = res.value;
			return this.res;
		}
	}
}

Flow.CUSTOME_CARDBOX_GENERATOR_MAP = CUSTOME_CARDBOX_GENERATOR_MAP;

module.exports = Flow;
